﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.FFTPack
{
    /// <summary>
    /// FFT transform of a complex periodic sequence. 
    /// </summary>
    [Serializable]
    public class ComplexDoubleFFT : ComplexDoubleFFTMixed
    {
        /// <summary>
        /// This field holds the norm factor.
        /// </summary>
        private double normFactor;

        /// <summary>
        /// This field holds the wave table.
        /// </summary>
        private readonly double[] wavetable;

        /// <summary>
        /// This field holds the dimension.
        /// </summary>
        private readonly int ndim;

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexDoubleFFT"/> class.
        /// </summary>
        /// <param name="n">The size of a complex data sequence. When n is a multiplication of small 
        /// numbers (4, 2, 3, 5), this FFT transform is very efficient.</param>
        public ComplexDoubleFFT(int n)
        {
            if (15 + 4 * n < 0)
            {
                throw new ArgumentException("15 + 4 * n < 0", "n");
            }

            ndim = n;
            normFactor = n;

            if (wavetable == null || wavetable.Length != (4 * ndim + 15))
            {
                wavetable = new double[4 * ndim + 15];
            }

            Cffti(ndim, wavetable);
        }

        /// <summary>
        /// This field can be used to normalize this FFT transform. This is because a call of forward 
        /// transform followed by a call of backward transform will multiply the input sequence by the 
        /// norm factor.
        /// </summary>
        /// <value>The norm factor to use for the transform.</value>
        public double NormFactor
        {
            get { return normFactor; }
            set { normFactor = value; }
        }

        /// <summary>
        /// Forward complex FFT transform. 
        /// </summary>
        /// <param name="x">2 * n real double data representing n complex double data. 
        /// As an input parameter, x is an array of 2 * n real data representing n complex data. 
        /// As an output parameter, x represents n FFT's complex data.</param>
        public void ForwardTransform(double[] x)
        {
            if (x == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Length != 2 * ndim)
            {
                throw new ArgumentException("The length of data can not match that of the wavetable");
            }
            Cfftf(ndim, x, wavetable);
        }

        /// <summary>
        /// Forward complex FFT transform.
        /// </summary>
        /// <param name="x">An array of n complex data.</param>
        public void ForwardTransform(Complex1D x)
        {
            if (x == (Complex1D) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.X == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Y == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Y.Length == 0)
            {
                throw new ArgumentException("x.Y.Length == 0", "x");
            }

            if (x.X.Length != ndim)
            {
                throw new ArgumentException("The length of data can not match that of the wavetable");
            }

            double[] y = new double[2 * ndim];

            for (int i = 0; i < ndim; i++)
            {
                y[2 * i] = x.X[i];
                y[2 * i + 1] = x.Y[i];
            }

            Cfftf(ndim, y, wavetable);

            for (int i = 0; i < ndim; i++)
            {
                x.X[i] = y[2 * i];
                x.Y[i] = y[2 * i + 1];
            }
        }

        /// <summary>
        /// Backward complex FFT transform. It is the unnormalized inverse transform of ForwardTransform()(double[]).
        /// </summary>
        /// <param name="x">2 * n real double data representing n complex double data. 
        /// As an input parameter, x is an array of 2 * n real data representing n complex data. 
        /// As an output parameter, x represents n FFT's complex data.</param>
        public void BackTransform(double[] x)
        {
            if (x == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Length != 2 * ndim)
            {
                throw new ArgumentException("The length of data can not match that of the wavetable");
            }
            Cfftb(ndim, x, wavetable);
        }

        /// <summary>
        /// Backward complex FFT transform. It is the unnormalized inverse transform of ForwardTransform()(Complex1D[]).
        /// </summary>
        /// <param name="x">An array of n complex data.</param>
        public void BackTransform(Complex1D x)
        {
            if (x == (Complex1D) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.X == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Y == (double[]) null)
            {
                throw new ArgumentNullException("x");
            }

            if (x.Y.Length == 0)
            {
                throw new ArgumentException("x.Y.Length == 0", "x");
            }

            if (x.X.Length != ndim)
            {
                throw new ArgumentException("The length of data can not match that of the wavetable");
            }

            double[] y = new double[2 * ndim];

            for (int i = 0; i < ndim; i++)
            {
                y[2 * i] = x.X[i];
                y[2 * i + 1] = x.Y[i];
            }

            Cfftb(ndim, y, wavetable);

            for (int i = 0; i < ndim; i++)
            {
                x.X[i] = y[2 * i];
                x.Y[i] = y[2 * i + 1];
            }
        }
    }
}